library(dplyr);
library(magrittr);
library(ggplot2);
library(lubridate);
library(readr);
df <- read.csv("./marathon_results_2017.csv", header=TRUE, stringsAsFactors=FALSE)
df <- df[c('Age', 'M.F', 'X5K', 'X10K', 'X15K', 'X20K', 'X25K', 'X30K', 'X35K', 'X40K', 'Official.Time')]
df %<>% filter(X5K != '-' & X10K != '-' & X15K != '-' & X20K != '-' & X25K != '-' & X30K != '-' & X35K != '-' & X40K != '-')
df
cols <- c('X5K', 'X10K', 'X15K', 'X20K', 'X25K', 'X30K', 'X35K', 'X40K')
df %<>% mutate_each_(funs(as.POSIXct(., format="%H:%M:%S")), cols);
`mutate_each()` is deprecated.
Use `mutate_all()`, `mutate_at()` or `mutate_if()` instead.
To map `funs` over a selection of variables, use `mutate_at()`
df$X40K <- as.numeric(difftime(df$X40K, df$X35K, units='secs'))
df$X35K <- as.numeric(difftime(df$X35K, df$X30K, units='secs'))
df$X30K <- as.numeric(difftime(df$X30K, df$X25K, units='secs'))
df$X25K <- as.numeric(difftime(df$X25K, df$X20K, units='secs'))
df$X20K <- as.numeric(difftime(df$X20K, df$X15K, units='secs'))
df$X15K <- as.numeric(difftime(df$X15K, df$X10K, units='secs'))
df$X10K <- as.numeric(difftime(df$X10K, df$X5K, units='secs'))
df$X5K <- as.numeric(difftime(df$X5K, as.POSIXct('00:00:00', format="%H:%M:%S"), units='secs'))
colnames(df)[colnames(df) == 'M.F'] <- 'Gender'
df
demo <- df %>%
  mutate(Gender, Gender = ifelse('M' == Gender,'MEN', 'WOMEN')) %>% 
  mutate(Age, Age = ifelse(Age > 40, 'OLD', 'YOUNG')) %>% 
  group_by(Gender, Age) %>% 
  count()
demo$comb <- paste(demo$Age, demo$Gender)
demo
# pie charts in gg plot are just too much work
#library(scales)
#ggplot(demo, aes(x="", y=n, fill=factor(comb)))+
#  geom_bar(width=1, stat="identity") +
#  scale_fill_manual(values=c("#3617ff", "#e048ce", "#45caff", "#ffc4fb")) + 
#  coord_polar("y", start=0) 
# Pie Chart with Percentages
slices <- demo$n
lbls <- demo$comb
pct <- round(slices/sum(slices)*100)
lbls <- paste(lbls, pct) # add percents to labels 
lbls <- paste(lbls,"%",sep="") # ad % to labels 
pie(slices, labels = lbls, col=c("blue", "cyan", "violet", "pink"), main="Distribution of gender and age")

Finishing times by gender

df$Official.Time <- as.POSIXct(df$Official.Time, format="%H:%M:%S")
ggplot(df, aes(df$Official.Time, fill = df$Gender)) +
  geom_histogram(aes(y=..density..), alpha=0.6, 
                 position="identity", lwd=0.2) +
  ggtitle("Normalized")

df %>%
  mutate(Age, Age = ifelse(Age > 40, 'OLD', 'YOUNG')) %>% 
  ggplot(aes(Official.Time, fill = Age)) +
    geom_histogram(aes(y=..density..), alpha=0.6, 
                 position="identity", lwd=0.2) +
  ggtitle("Normalized")

n_groups <- 20
zebra_colormap <- rep(c("darkcyan", "cyan"), 20)
df <- df[df$Official.Time < quantile(df$Official.Time, 0.99), ]
#splits = split(df, cut(df$Official.Time, N))  # Time splits
#splits <- split(df, rep(1:ceiling(nrow(df)/N), each=N, length.out=nrow(df)))  # N marathoners splits
#df$group <- rep(1:ceiling(nrow(df)/n_groups), each=nrow(df)/n_groups, length.out=nrow(df))  # N marathoners splits
df$Official.Time <- as.numeric(difftime(df$Official.Time, as.POSIXct('00:00:00', format="%H:%M:%S"), units='mins'))
df$group <- cut(df$Official.Time, n_groups)
ggplot(df) + 
  geom_point(aes(x=1:NROW(df), y=df$Official.Time,  col=as.factor(df$group))) +
  scale_color_manual(values=zebra_colormap) +
  theme_bw()

Are women more disciplined than men?

women <- df %>%
  filter(Gender == 'F')
men <- df %>%
  filter(Gender == 'M')
b_splits = split(df, df$group)  # Time splits
w_splits = split(women, women$group)  # Time splits
m_splits = split(men, men$group)  # Time splits
g_sd_df <- data.frame("group" = numeric(0),
                      "gender" = character(0),
                      "n" = numeric(0),
                      "mean_sd" = numeric(0),
                      "sd_sd" = numeric(0),
                      stringsAsFactors = FALSE)
for (i in 1:n_groups) {
  gender <- 'M'
  mean_sd <- as.numeric(m_splits[[i]] %>%
                    select(cols) %>%
                    transform(SD=apply(., 1, sd, na.rm = TRUE)) %>%
                    summarize(sample_sd = mean(SD, na.rm = TRUE), sd(SD, na.rm = TRUE)))
  n <- as.numeric(m_splits[[i]] %>%
                    select(Official.Time) %>%
                    summarize(n = n()))
  g_sd_df[nrow(g_sd_df) + 1,] = c(i, gender, n, mean_sd, sd_sd)
  
  
  gender <- 'F'
  mean_sd <- as.numeric(w_splits[[i]] %>%
                    select(cols) %>%
                    transform(SD=apply(., 1, sd, na.rm = TRUE)) %>%
                    summarize(sample_sd = mean(SD, na.rm = TRUE), sd(SD, na.rm = TRUE)))
  n <- as.numeric(w_splits[[i]] %>%
                    select(Official.Time) %>%
                    summarize(n = n()))
  if (n != 0) {
    g_sd_df[nrow(g_sd_df) + 1,] = c(i, gender, n, mean_sd, sd_sd)
  }
  gender <- 'B'
  mean_sd <- as.numeric(b_splits[[i]] %>%
                    select(cols) %>%
                    transform(SD=apply(., 1, sd, na.rm = TRUE)) %>%
                    summarize(sample_sd = mean(SD, na.rm = TRUE)))
  n <- as.numeric(b_splits[[i]] %>%
                    select(Official.Time) %>%
                    summarize(n = n()))
  g_sd_df[nrow(g_sd_df) + 1,] = c(i, gender, n, mean_sd, sd_sd)
  
}
data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]data length [6] is not a sub-multiple or multiple of the number of columns [5]
g_sd_df$n <- as.numeric(g_sd_df$n)
g_sd_df$mean_sd <- as.numeric(g_sd_df$mean_sd)
g_sd_df$sd_sd <- as.numeric(g_sd_df$sd_sd)
g_sd_df$group <- as.numeric(g_sd_df$group)
g_sd_df
ggplot(g_sd_df) +
  geom_point(aes(x=group, y=mean_sd, colour=gender)) +
  scale_color_manual(values=c('black', 'violet', 'blue')) +
  theme_bw()

g_sd_df <- filter(g_sd_df, g_sd_df$gender == 'M' | g_sd_df$gender == 'F')
g_sd_df$error <- (1.96 * g_sd_df$sd_sd) / sqrt(g_sd_df$n) 
g_sd_df
ggplot(g_sd_df, aes(x=group, y=mean_sd, colour=gender)) +
  geom_point() +
  geom_errorbar(aes(ymin=mean_sd - error, ymax=mean_sd + error)) +
  scale_color_manual(values=c('violet', 'blue')) +
  theme_bw()

if (!file.exists('animation.gif')) {
  library(animation)
  n_samples <- 20
  sample <- df %>%
    group_by(group) %>%
    sample_n(n_samples, replace=TRUE)
  
  times <- t(data.matrix(select(sample, cols)))
  
  makeplot <- function() {
    for(i in 1:nrow(sample)) {
    
      plot.ts(times[cols,1:i], 
              plot.type="single", 
              lwd=0.5, 
              col=rep(rainbow(n_groups), each=n_samples), 
              ylim=c(900, 3000), 
              xlab='', ylab='', axes = F)
      
      lines(times[cols,i], 
            lwd=2, col=1, 
            xlab='', ylab='', axes = F)
      
      title(main="5K pace analysis", sub=paste('Group rank #', as.character(ceiling(i/n_samples))), xlab="", ylab="Split time (seconds)")
      axis(side=2,at=c(800, 1000, 1500, 2000, 2500, 3000),labels=c('800', '1000', '1500', '2000', '2500', '3000'))
      axis(side=1,at=c(-10,1,2,3,4,5,6,7,8),labels=c('','5K', '10K', '15K', '20K', '25K', '30K', '35K', '40K'))
    }
  }
  oopt = ani.options(interval = 0, nmax = n_runners)
  saveGIF(makeplot(),interval = 0.1, width = 580, height = 400)
  ani.options(oopt)
}
corr <- df %>%
  select(cols) %>%
  transform(SD=apply(., 1, sd, na.rm = TRUE)) %>%
  select(c('SD')) %>%
  as.data.frame()
corr <- bind_cols(corr, select(df, c('Official.Time')))
ggplot(corr, aes(x=Official.Time, y=SD)) +
  #stat_density_2d(geom = "raster", aes(fill = ..density..), contour = FALSE)
  #stat_density_2d(aes(fill = ..level..), geom = "polygon")
  #stat_density_2d(geom = "point", aes(size = ..density..), n = 20, contour = FALSE)
  geom_hex(binwidth = c(1, 20)) + 
  theme_bw()

NA
LS0tCnRpdGxlOiAiMjAxNyBCb3N0b24gTWFyYXRob24gQW5hbHlzaXMiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCmBgYHtyfQpsaWJyYXJ5KGRwbHlyKTsKbGlicmFyeShtYWdyaXR0cik7CmxpYnJhcnkoZ2dwbG90Mik7CmxpYnJhcnkobHVicmlkYXRlKTsKbGlicmFyeShyZWFkcik7CmBgYAoKYGBge3J9CmRmIDwtIHJlYWQuY3N2KCIuL21hcmF0aG9uX3Jlc3VsdHNfMjAxNy5jc3YiLCBoZWFkZXI9VFJVRSwgc3RyaW5nc0FzRmFjdG9ycz1GQUxTRSkKZGYgPC0gZGZbYygnQWdlJywgJ00uRicsICdYNUsnLCAnWDEwSycsICdYMTVLJywgJ1gyMEsnLCAnWDI1SycsICdYMzBLJywgJ1gzNUsnLCAnWDQwSycsICdPZmZpY2lhbC5UaW1lJyldCmRmICU8PiUgZmlsdGVyKFg1SyAhPSAnLScgJiBYMTBLICE9ICctJyAmIFgxNUsgIT0gJy0nICYgWDIwSyAhPSAnLScgJiBYMjVLICE9ICctJyAmIFgzMEsgIT0gJy0nICYgWDM1SyAhPSAnLScgJiBYNDBLICE9ICctJykKZGYKYGBgCgpgYGB7cn0KY29scyA8LSBjKCdYNUsnLCAnWDEwSycsICdYMTVLJywgJ1gyMEsnLCAnWDI1SycsICdYMzBLJywgJ1gzNUsnLCAnWDQwSycpCmRmICU8PiUgbXV0YXRlX2VhY2hfKGZ1bnMoYXMuUE9TSVhjdCguLCBmb3JtYXQ9IiVIOiVNOiVTIikpLCBjb2xzKTsKCmRmJFg0MEsgPC0gYXMubnVtZXJpYyhkaWZmdGltZShkZiRYNDBLLCBkZiRYMzVLLCB1bml0cz0nc2VjcycpKQpkZiRYMzVLIDwtIGFzLm51bWVyaWMoZGlmZnRpbWUoZGYkWDM1SywgZGYkWDMwSywgdW5pdHM9J3NlY3MnKSkKZGYkWDMwSyA8LSBhcy5udW1lcmljKGRpZmZ0aW1lKGRmJFgzMEssIGRmJFgyNUssIHVuaXRzPSdzZWNzJykpCmRmJFgyNUsgPC0gYXMubnVtZXJpYyhkaWZmdGltZShkZiRYMjVLLCBkZiRYMjBLLCB1bml0cz0nc2VjcycpKQpkZiRYMjBLIDwtIGFzLm51bWVyaWMoZGlmZnRpbWUoZGYkWDIwSywgZGYkWDE1SywgdW5pdHM9J3NlY3MnKSkKZGYkWDE1SyA8LSBhcy5udW1lcmljKGRpZmZ0aW1lKGRmJFgxNUssIGRmJFgxMEssIHVuaXRzPSdzZWNzJykpCmRmJFgxMEsgPC0gYXMubnVtZXJpYyhkaWZmdGltZShkZiRYMTBLLCBkZiRYNUssIHVuaXRzPSdzZWNzJykpCmRmJFg1SyA8LSBhcy5udW1lcmljKGRpZmZ0aW1lKGRmJFg1SywgYXMuUE9TSVhjdCgnMDA6MDA6MDAnLCBmb3JtYXQ9IiVIOiVNOiVTIiksIHVuaXRzPSdzZWNzJykpCmNvbG5hbWVzKGRmKVtjb2xuYW1lcyhkZikgPT0gJ00uRiddIDwtICdHZW5kZXInCmRmCmBgYAoKCmBgYHtyfQpkZW1vIDwtIGRmICU+JQogIG11dGF0ZShHZW5kZXIsIEdlbmRlciA9IGlmZWxzZSgnTScgPT0gR2VuZGVyLCdNRU4nLCAnV09NRU4nKSkgJT4lIAogIG11dGF0ZShBZ2UsIEFnZSA9IGlmZWxzZShBZ2UgPiA0MCwgJ09MRCcsICdZT1VORycpKSAlPiUgCiAgZ3JvdXBfYnkoR2VuZGVyLCBBZ2UpICU+JSAKICBjb3VudCgpCgpkZW1vJGNvbWIgPC0gcGFzdGUoZGVtbyRBZ2UsIGRlbW8kR2VuZGVyKQpkZW1vCgojIHBpZSBjaGFydHMgaW4gZ2cgcGxvdCBhcmUganVzdCB0b28gbXVjaCB3b3JrCiNsaWJyYXJ5KHNjYWxlcykKI2dncGxvdChkZW1vLCBhZXMoeD0iIiwgeT1uLCBmaWxsPWZhY3Rvcihjb21iKSkpKwojICBnZW9tX2Jhcih3aWR0aD0xLCBzdGF0PSJpZGVudGl0eSIpICsKIyAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIiMzNjE3ZmYiLCAiI2UwNDhjZSIsICIjNDVjYWZmIiwgIiNmZmM0ZmIiKSkgKyAKIyAgY29vcmRfcG9sYXIoInkiLCBzdGFydD0wKSAKYGBgCgpgYGB7cn0KIyBQaWUgQ2hhcnQgd2l0aCBQZXJjZW50YWdlcwpzbGljZXMgPC0gZGVtbyRuCmxibHMgPC0gZGVtbyRjb21iCnBjdCA8LSByb3VuZChzbGljZXMvc3VtKHNsaWNlcykqMTAwKQpsYmxzIDwtIHBhc3RlKGxibHMsIHBjdCkgIyBhZGQgcGVyY2VudHMgdG8gbGFiZWxzIApsYmxzIDwtIHBhc3RlKGxibHMsIiUiLHNlcD0iIikgIyBhZCAlIHRvIGxhYmVscyAKcGllKHNsaWNlcywgbGFiZWxzID0gbGJscywgY29sPWMoImJsdWUiLCAiY3lhbiIsICJ2aW9sZXQiLCAicGluayIpLCBtYWluPSJEaXN0cmlidXRpb24gb2YgZ2VuZGVyIGFuZCBhZ2UiKQpgYGAKCgpGaW5pc2hpbmcgdGltZXMgYnkgZ2VuZGVyCgpgYGB7cn0KZGYkT2ZmaWNpYWwuVGltZSA8LSBhcy5QT1NJWGN0KGRmJE9mZmljaWFsLlRpbWUsIGZvcm1hdD0iJUg6JU06JVMiKQpnZ3Bsb3QoZGYsIGFlcyhkZiRPZmZpY2lhbC5UaW1lLCBmaWxsID0gZGYkR2VuZGVyKSkgKwogIGdlb21faGlzdG9ncmFtKGFlcyh5PS4uZGVuc2l0eS4uKSwgYWxwaGE9MC42LCAKICAgICAgICAgICAgICAgICBwb3NpdGlvbj0iaWRlbnRpdHkiLCBsd2Q9MC4yKSArCiAgZ2d0aXRsZSgiTm9ybWFsaXplZCIpCmBgYAoKCmBgYHtyfQpkZiAlPiUKICBtdXRhdGUoQWdlLCBBZ2UgPSBpZmVsc2UoQWdlID4gNDAsICdPTEQnLCAnWU9VTkcnKSkgJT4lIAogIGdncGxvdChhZXMoT2ZmaWNpYWwuVGltZSwgZmlsbCA9IEFnZSkpICsKICAgIGdlb21faGlzdG9ncmFtKGFlcyh5PS4uZGVuc2l0eS4uKSwgYWxwaGE9MC42LCAKICAgICAgICAgICAgICAgICBwb3NpdGlvbj0iaWRlbnRpdHkiLCBsd2Q9MC4yKSArCiAgZ2d0aXRsZSgiTm9ybWFsaXplZCIpCgpgYGAKCgoKYGBge3J9Cm5fZ3JvdXBzIDwtIDIwCnplYnJhX2NvbG9ybWFwIDwtIHJlcChjKCJkYXJrY3lhbiIsICJjeWFuIiksIDIwKQoKZGYgPC0gZGZbZGYkT2ZmaWNpYWwuVGltZSA8IHF1YW50aWxlKGRmJE9mZmljaWFsLlRpbWUsIDAuOTkpLCBdCgoKI3NwbGl0cyA9IHNwbGl0KGRmLCBjdXQoZGYkT2ZmaWNpYWwuVGltZSwgTikpICAjIFRpbWUgc3BsaXRzCiNzcGxpdHMgPC0gc3BsaXQoZGYsIHJlcCgxOmNlaWxpbmcobnJvdyhkZikvTiksIGVhY2g9TiwgbGVuZ3RoLm91dD1ucm93KGRmKSkpICAjIE4gbWFyYXRob25lcnMgc3BsaXRzCiNkZiRncm91cCA8LSByZXAoMTpjZWlsaW5nKG5yb3coZGYpL25fZ3JvdXBzKSwgZWFjaD1ucm93KGRmKS9uX2dyb3VwcywgbGVuZ3RoLm91dD1ucm93KGRmKSkgICMgTiBtYXJhdGhvbmVycyBzcGxpdHMKZGYkT2ZmaWNpYWwuVGltZSA8LSBhcy5udW1lcmljKGRpZmZ0aW1lKGRmJE9mZmljaWFsLlRpbWUsIGFzLlBPU0lYY3QoJzAwOjAwOjAwJywgZm9ybWF0PSIlSDolTTolUyIpLCB1bml0cz0nbWlucycpKQpkZiRncm91cCA8LSBjdXQoZGYkT2ZmaWNpYWwuVGltZSwgbl9ncm91cHMpCgpnZ3Bsb3QoZGYpICsgCiAgZ2VvbV9wb2ludChhZXMoeD0xOk5ST1coZGYpLCB5PWRmJE9mZmljaWFsLlRpbWUsICBjb2w9YXMuZmFjdG9yKGRmJGdyb3VwKSkpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPXplYnJhX2NvbG9ybWFwKSArCiAgdGhlbWVfYncoKQpgYGAKCgoKQXJlIHdvbWVuIG1vcmUgZGlzY2lwbGluZWQgdGhhbiBtZW4/CmBgYHtyfQp3b21lbiA8LSBkZiAlPiUKICBmaWx0ZXIoR2VuZGVyID09ICdGJykKCm1lbiA8LSBkZiAlPiUKICBmaWx0ZXIoR2VuZGVyID09ICdNJykKCmJfc3BsaXRzID0gc3BsaXQoZGYsIGRmJGdyb3VwKSAgIyBUaW1lIHNwbGl0cwp3X3NwbGl0cyA9IHNwbGl0KHdvbWVuLCB3b21lbiRncm91cCkgICMgVGltZSBzcGxpdHMKbV9zcGxpdHMgPSBzcGxpdChtZW4sIG1lbiRncm91cCkgICMgVGltZSBzcGxpdHMKCgpnX3NkX2RmIDwtIGRhdGEuZnJhbWUoImdyb3VwIiA9IG51bWVyaWMoMCksCiAgICAgICAgICAgICAgICAgICAgICAiZ2VuZGVyIiA9IGNoYXJhY3RlcigwKSwKICAgICAgICAgICAgICAgICAgICAgICJuIiA9IG51bWVyaWMoMCksCiAgICAgICAgICAgICAgICAgICAgICAibWVhbl9zZCIgPSBudW1lcmljKDApLAogICAgICAgICAgICAgICAgICAgICAgInNkX3NkIiA9IG51bWVyaWMoMCksCiAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCgpmb3IgKGkgaW4gMTpuX2dyb3VwcykgewogIGdlbmRlciA8LSAnTScKICBtZWFuX3NkIDwtIGFzLm51bWVyaWMobV9zcGxpdHNbW2ldXSAlPiUKICAgICAgICAgICAgICAgICAgICBzZWxlY3QoY29scykgJT4lCiAgICAgICAgICAgICAgICAgICAgdHJhbnNmb3JtKFNEPWFwcGx5KC4sIDEsIHNkLCBuYS5ybSA9IFRSVUUpKSAlPiUKICAgICAgICAgICAgICAgICAgICBzdW1tYXJpemUoc2FtcGxlX3NkID0gbWVhbihTRCwgbmEucm0gPSBUUlVFKSwgc2QoU0QsIG5hLnJtID0gVFJVRSkpKQogIG4gPC0gYXMubnVtZXJpYyhtX3NwbGl0c1tbaV1dICU+JQogICAgICAgICAgICAgICAgICAgIHNlbGVjdChPZmZpY2lhbC5UaW1lKSAlPiUKICAgICAgICAgICAgICAgICAgICBzdW1tYXJpemUobiA9IG4oKSkpCiAgZ19zZF9kZltucm93KGdfc2RfZGYpICsgMSxdID0gYyhpLCBnZW5kZXIsIG4sIG1lYW5fc2QsIHNkX3NkKQogIAogIAogIGdlbmRlciA8LSAnRicKICBtZWFuX3NkIDwtIGFzLm51bWVyaWMod19zcGxpdHNbW2ldXSAlPiUKICAgICAgICAgICAgICAgICAgICBzZWxlY3QoY29scykgJT4lCiAgICAgICAgICAgICAgICAgICAgdHJhbnNmb3JtKFNEPWFwcGx5KC4sIDEsIHNkLCBuYS5ybSA9IFRSVUUpKSAlPiUKICAgICAgICAgICAgICAgICAgICBzdW1tYXJpemUoc2FtcGxlX3NkID0gbWVhbihTRCwgbmEucm0gPSBUUlVFKSwgc2QoU0QsIG5hLnJtID0gVFJVRSkpKQogIG4gPC0gYXMubnVtZXJpYyh3X3NwbGl0c1tbaV1dICU+JQogICAgICAgICAgICAgICAgICAgIHNlbGVjdChPZmZpY2lhbC5UaW1lKSAlPiUKICAgICAgICAgICAgICAgICAgICBzdW1tYXJpemUobiA9IG4oKSkpCiAgaWYgKG4gIT0gMCkgewogICAgZ19zZF9kZltucm93KGdfc2RfZGYpICsgMSxdID0gYyhpLCBnZW5kZXIsIG4sIG1lYW5fc2QsIHNkX3NkKQogIH0KCiAgZ2VuZGVyIDwtICdCJwogIG1lYW5fc2QgPC0gYXMubnVtZXJpYyhiX3NwbGl0c1tbaV1dICU+JQogICAgICAgICAgICAgICAgICAgIHNlbGVjdChjb2xzKSAlPiUKICAgICAgICAgICAgICAgICAgICB0cmFuc2Zvcm0oU0Q9YXBwbHkoLiwgMSwgc2QsIG5hLnJtID0gVFJVRSkpICU+JQogICAgICAgICAgICAgICAgICAgIHN1bW1hcml6ZShzYW1wbGVfc2QgPSBtZWFuKFNELCBuYS5ybSA9IFRSVUUpKSkKICBuIDwtIGFzLm51bWVyaWMoYl9zcGxpdHNbW2ldXSAlPiUKICAgICAgICAgICAgICAgICAgICBzZWxlY3QoT2ZmaWNpYWwuVGltZSkgJT4lCiAgICAgICAgICAgICAgICAgICAgc3VtbWFyaXplKG4gPSBuKCkpKQogIGdfc2RfZGZbbnJvdyhnX3NkX2RmKSArIDEsXSA9IGMoaSwgZ2VuZGVyLCBuLCBtZWFuX3NkLCBzZF9zZCkKICAKfQoKCmdfc2RfZGYkbiA8LSBhcy5udW1lcmljKGdfc2RfZGYkbikKZ19zZF9kZiRtZWFuX3NkIDwtIGFzLm51bWVyaWMoZ19zZF9kZiRtZWFuX3NkKQpnX3NkX2RmJHNkX3NkIDwtIGFzLm51bWVyaWMoZ19zZF9kZiRzZF9zZCkKZ19zZF9kZiRncm91cCA8LSBhcy5udW1lcmljKGdfc2RfZGYkZ3JvdXApCmdfc2RfZGYKYGBgCgoKYGBge3J9CmdncGxvdChnX3NkX2RmKSArCiAgZ2VvbV9wb2ludChhZXMoeD1ncm91cCwgeT1tZWFuX3NkLCBjb2xvdXI9Z2VuZGVyKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygnYmxhY2snLCAndmlvbGV0JywgJ2JsdWUnKSkgKwogIHRoZW1lX2J3KCkKYGBgCgpgYGB7cn0KZ19zZF9kZiA8LSBmaWx0ZXIoZ19zZF9kZiwgZ19zZF9kZiRnZW5kZXIgPT0gJ00nIHwgZ19zZF9kZiRnZW5kZXIgPT0gJ0YnKQpnX3NkX2RmJGVycm9yIDwtICgxLjk2ICogZ19zZF9kZiRzZF9zZCkgLyBzcXJ0KGdfc2RfZGYkbikgCmdfc2RfZGYKCmdncGxvdChnX3NkX2RmLCBhZXMoeD1ncm91cCwgeT1tZWFuX3NkLCBjb2xvdXI9Z2VuZGVyKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbj1tZWFuX3NkIC0gZXJyb3IsIHltYXg9bWVhbl9zZCArIGVycm9yKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygndmlvbGV0JywgJ2JsdWUnKSkgKwogIHRoZW1lX2J3KCkKCmBgYAoKCgoKYGBge3J9CmlmICghZmlsZS5leGlzdHMoJ2FuaW1hdGlvbi5naWYnKSkgewogIGxpYnJhcnkoYW5pbWF0aW9uKQoKICBuX3NhbXBsZXMgPC0gMjAKICBzYW1wbGUgPC0gZGYgJT4lCiAgICBncm91cF9ieShncm91cCkgJT4lCiAgICBzYW1wbGVfbihuX3NhbXBsZXMsIHJlcGxhY2U9VFJVRSkKICAKICB0aW1lcyA8LSB0KGRhdGEubWF0cml4KHNlbGVjdChzYW1wbGUsIGNvbHMpKSkKICAKICBtYWtlcGxvdCA8LSBmdW5jdGlvbigpIHsKICAgIGZvcihpIGluIDE6bnJvdyhzYW1wbGUpKSB7CiAgICAKICAgICAgcGxvdC50cyh0aW1lc1tjb2xzLDE6aV0sIAogICAgICAgICAgICAgIHBsb3QudHlwZT0ic2luZ2xlIiwgCiAgICAgICAgICAgICAgbHdkPTAuNSwgCiAgICAgICAgICAgICAgY29sPXJlcChyYWluYm93KG5fZ3JvdXBzKSwgZWFjaD1uX3NhbXBsZXMpLCAKICAgICAgICAgICAgICB5bGltPWMoOTAwLCAzMDAwKSwgCiAgICAgICAgICAgICAgeGxhYj0nJywgeWxhYj0nJywgYXhlcyA9IEYpCiAgICAgIAogICAgICBsaW5lcyh0aW1lc1tjb2xzLGldLCAKICAgICAgICAgICAgbHdkPTIsIGNvbD0xLCAKICAgICAgICAgICAgeGxhYj0nJywgeWxhYj0nJywgYXhlcyA9IEYpCiAgICAgIAogICAgICB0aXRsZShtYWluPSI1SyBwYWNlIGFuYWx5c2lzIiwgc3ViPXBhc3RlKCdHcm91cCByYW5rICMnLCBhcy5jaGFyYWN0ZXIoY2VpbGluZyhpL25fc2FtcGxlcykpKSwgeGxhYj0iIiwgeWxhYj0iU3BsaXQgdGltZSAoc2Vjb25kcykiKQogICAgICBheGlzKHNpZGU9MixhdD1jKDgwMCwgMTAwMCwgMTUwMCwgMjAwMCwgMjUwMCwgMzAwMCksbGFiZWxzPWMoJzgwMCcsICcxMDAwJywgJzE1MDAnLCAnMjAwMCcsICcyNTAwJywgJzMwMDAnKSkKICAgICAgYXhpcyhzaWRlPTEsYXQ9YygtMTAsMSwyLDMsNCw1LDYsNyw4KSxsYWJlbHM9YygnJywnNUsnLCAnMTBLJywgJzE1SycsICcyMEsnLCAnMjVLJywgJzMwSycsICczNUsnLCAnNDBLJykpCiAgICB9CiAgfQogIG9vcHQgPSBhbmkub3B0aW9ucyhpbnRlcnZhbCA9IDAsIG5tYXggPSBuX3J1bm5lcnMpCiAgc2F2ZUdJRihtYWtlcGxvdCgpLGludGVydmFsID0gMC4xLCB3aWR0aCA9IDU4MCwgaGVpZ2h0ID0gNDAwKQogIGFuaS5vcHRpb25zKG9vcHQpCn0KYGBgCgoKIVtdKGFuaW1hdGlvbi5naWYpCgogCgoKYGBge3J9CmNvcnIgPC0gZGYgJT4lCiAgc2VsZWN0KGNvbHMpICU+JQogIHRyYW5zZm9ybShTRD1hcHBseSguLCAxLCBzZCwgbmEucm0gPSBUUlVFKSkgJT4lCiAgc2VsZWN0KGMoJ1NEJykpICU+JQogIGFzLmRhdGEuZnJhbWUoKQoKCmNvcnIgPC0gYmluZF9jb2xzKGNvcnIsIHNlbGVjdChkZiwgYygnT2ZmaWNpYWwuVGltZScpKSkKCmdncGxvdChjb3JyLCBhZXMoeD1PZmZpY2lhbC5UaW1lLCB5PVNEKSkgKwogICNzdGF0X2RlbnNpdHlfMmQoZ2VvbSA9ICJyYXN0ZXIiLCBhZXMoZmlsbCA9IC4uZGVuc2l0eS4uKSwgY29udG91ciA9IEZBTFNFKQogICNzdGF0X2RlbnNpdHlfMmQoYWVzKGZpbGwgPSAuLmxldmVsLi4pLCBnZW9tID0gInBvbHlnb24iKQogICNzdGF0X2RlbnNpdHlfMmQoZ2VvbSA9ICJwb2ludCIsIGFlcyhzaXplID0gLi5kZW5zaXR5Li4pLCBuID0gMjAsIGNvbnRvdXIgPSBGQUxTRSkKICBnZW9tX2hleChiaW53aWR0aCA9IGMoMSwgMjApKSArIAogIHRoZW1lX2J3KCkKICAKYGBgCgoKCgoK